package cn.bugstack.springframework.beans.factory.support;

import cn.bugstack.springframework.beans.BeansException;
import cn.bugstack.springframework.beans.factory.DisposableBean;
import cn.bugstack.springframework.beans.factory.config.SingletonBeanRegistry;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 默认单例bean注册器类，实现了SingletonBeanRegistry接口。
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    /**
     * 这段代码定义了一个静态的 NULL_OBJECT 常量，用作表示空单例对象的标记值。
     * 在 Spring 框架的单例缓存中，有时需要使用 ConcurrentHashMap 等并发 Map 来存储单例对象，
     * 而这些 Map 是不支持存储 null 值的。
     * 因此，为了表示空单例对象，Spring 使用了一个特殊的标记对象 NULL_OBJECT。
     * 当需要存储空单例对象时，可以将 NULL_OBJECT 对象放入 Map 中作为其值。
     * 在获取缓存中的单例对象时，如果发现其值为 NULL_OBJECT，则表示实际上这个单例对象是空的。
     */
    protected static final Object NULL_OBJECT = new Object();

    /**
     * 这段代码定义了一个 singletonObjects 字段，它是一个 HashMap 类型的实例变量，用于存储单例对象的映射关系。
     * 在 Spring 容器中，单例对象是指只会被创建一次并且在整个容器生命周期内都存在的对象。
     * 这个 singletonObjects Map 的键为单例对象的名称，值为对应的单例对象实例。
     * 当客户端代码需要获取某个单例对象时，Spring 会首先检查这个 Map 中是否已经存在该单例对象的实例。
     * 如果存在，则直接返回；如果不存在，则会创建该单例对象的实例并放入这个 Map 中，以便下次直接获取。
     * 这个 singletonObjects Map 是 Spring 中用于管理单例对象的核心数据结构之一。
     */
    private Map<String, Object> singletonObjects = new HashMap<>();

    /**
     * 这个 disposableBeans Map 用于存储这些可销毁的 Bean，键为 Bean 的名称，值为对应的 Bean 实例。
     * 在容器关闭时，Spring 会遍历这个 Map，依次调用每个 Bean 的 destroy() 方法，从而执行它们的清理逻辑。
     */
    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    /**
     * 获取指定名称的单例对象。
     *
     * @param beanName 要获取的bean的名称。
     * @return 如果找到，则返回对应的单例对象；否则返回null。
     */
    @Override
    public Object getSingleton(String beanName) {
        return singletonObjects.get(beanName);
    }

    /**
     * 注册一个单例对象。
     *
     * @param beanName 要注册的bean的名称。
     * @param singletonObject 要注册的单例对象。
     */
    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
    }

    protected void addSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    /**
     * 方法首先获取所有待销毁的单例对象的键集合，然后将其转换为数组。
     * 接着，通过遍历这个数组，依次从 disposableBeans Map 中移除单例对象，并调用它们的 destroy() 方法进行销毁。
     * 在销毁过程中，如果发生异常，会捕获并封装为 BeansException 异常抛出。
     * 整个过程保证了单例对象的销毁顺序与它们注册时的顺序相反，即后注册的先销毁，前注册的后销毁。
     * 这样可以避免由于对象间的依赖关系而导致的资源释放顺序问题。
     */
    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }
}
